package edu.washington.cs.sounddetector.server.database;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.jdo.PersistenceManager;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import javax.jdo.annotations.Queries;
import javax.jdo.annotations.Query;

import edu.washington.cs.sounddetector.server.api.APIConsts;
import edu.washington.cs.sounddetector.server.sound.SoundHelper;
import edu.washington.cs.sounddetector.server.sound.SoundProcessorBuilder;

/**
 * Database table for saving sound classes.
 * 
 * @author the.dylan.price@gmail.com
 * 
 */
@PersistenceCapable
@Queries({
        @Query(
                name = "QUERY_BY_USER_DETECTOR",
                language = "JDOQL",
                value = "SELECT FROM edu.washington.cs.sounddetector.server.database.DbSoundClass WHERE userId == :userId && detectorId == :detectorId"),
        @Query(
                name = "QUERY_BY_USER_SOUNDCLASS_DETECTOR",
                language = "JDOQL",
                value = "SELECT UNIQUE FROM edu.washington.cs.sounddetector.server.database.DbSoundClass WHERE userId == :userId && soundClass == :soundClass && detectorId == :detectorId") })
public class DbSoundClass
{
    /**
     * Named Query.
     * 
     * @param userId
     *            (String)
     * @param detectorId
     *            (String)
     */
    public static final String QUERY_BY_USER_DETECTOR = "QUERY_BY_USER_DETECTOR";

    /**
     * Named Query.
     * 
     * @param userId
     *            (String)
     * @param soundClass
     *            (String)
     * @param detectorId
     *            (String)
     */
    public static final String QUERY_BY_USER_SOUNDCLASS_DETECTOR = "QUERY_BY_USER_SOUNDCLASS_DETECTOR";

    public static final String UNKNOWN = APIConsts.UNKNOWN_SOUND_CLASS;
    private final static Map<String, Boolean> createUnknownClassCalled = new HashMap<String, Boolean>();

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.NATIVE)
    private long id;
    private String soundClass;
    private String detectorId;
    private String userId;
    private int rgbColor;

    public DbSoundClass(String soundClass, String detectorId)
    {
        this.soundClass = soundClass;
        this.detectorId = detectorId;
        // TODO: change when we implement users
        this.userId = "1";
        this.rgbColor = -1;
    }

    /**
     * @return the id
     */
    public long getId()
    {
        return id;
    }

    /**
     * @return the soundClass
     */
    public String getSoundClass()
    {
        return soundClass;
    }

    /**
     * @param soundClass
     *            the soundClass to set
     */
    public void setSoundClass(String soundClass)
    {
        this.soundClass = soundClass;
    }

    /**
     * @return the detectorId
     */
    public String getDetectorId()
    {
        return this.detectorId;
    }

    /**
     * @param detectorId
     *            the detectorId to set
     */
    public void setDetectorId(String detectorId)
    {
        this.detectorId = detectorId;
    }

    /**
     * @return the rgbColor
     */
    public int getRgbColor()
    {
        return rgbColor;
    }

    /**
     * @param rgbColor
     *            the rgbColor to set
     */
    public void setRgbColor(int rgbColor)
    {
        this.rgbColor = rgbColor;
    }

    /**
     * @return the userId
     */
    public String getUserId()
    {
        return userId;
    }

    /**
     * @param userId
     *            the userId to set
     */
    public void setUserId(String userId)
    {
        this.userId = userId;
    }

    /**
     * Creates the {@link #UNKNOWN} DbSoundClass in the database, but only if it does not exist, as
     * well as ensuring there are random noise samples for the class. If this method has been called
     * before during the lifetime of this JVM, does nothing.
     */
    public static void createUnknownClass(PersistenceManager pm, String userId, String detectorId,
            SoundProcessorBuilder spb)
    {
        if (createUnknownClassCalled.get(detectorId) == null)
        {
            javax.jdo.Query query = pm.newNamedQuery(DbSoundClass.class,
                    QUERY_BY_USER_SOUNDCLASS_DETECTOR);
            DbSoundClass soundClass = (DbSoundClass) query.execute(userId, UNKNOWN, detectorId);
            if (soundClass == null)
            {
                soundClass = new DbSoundClass(UNKNOWN, detectorId);
                pm.makePersistent(soundClass);
                List<Sound> sounds = SoundHelper.getRandomNoises(spb, soundClass);
                pm.makePersistentAll(sounds);
            }
            createUnknownClassCalled.put(detectorId, true);
        }
    }

    /**
     * Convenience method for executing {@link #QUERY_BY_USER_DETECTOR} and getting all the sound
     * class values.
     */
    public static Collection<DbSoundClass> query(PersistenceManager pm, String userId,
            String detectorId)
    {
        javax.jdo.Query query = pm.newNamedQuery(DbSoundClass.class,
                DbSoundClass.QUERY_BY_USER_DETECTOR);
        @SuppressWarnings("unchecked")
        Collection<DbSoundClass> results = (Collection<DbSoundClass>) query.execute(userId,
                detectorId);
        assert results.size() > 1;
        return results;
    }

    /**
     * Convenience method for executing {@link #QUERY_BY_SOUNDCLASS_DETECTOR}.
     * 
     * @return the SoundClass result from the query.
     */
    public static DbSoundClass query(PersistenceManager pm, String userId, String soundClass,
            String detectorId)
    {
        javax.jdo.Query query = pm.newNamedQuery(DbSoundClass.class,
                DbSoundClass.QUERY_BY_USER_SOUNDCLASS_DETECTOR);
        DbSoundClass sc = (DbSoundClass) query.execute(userId, soundClass, detectorId);
        return sc;
    }

    public static Collection<String> extractSoundClassNames(Collection<DbSoundClass> soundClasses)
    {
        List<String> soundClassNames = new ArrayList<String>();
        for (DbSoundClass dbsc : soundClasses)
            soundClassNames.add(dbsc.getSoundClass());
        return soundClassNames;
    }

}
